package com.excilys.formation.projet.ebanking.service.impl;

import java.security.AccessControlException;
import java.util.List;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.excilys.formation.projet.ebanking.dao.api.ICompteDao;
import com.excilys.formation.projet.ebanking.dao.api.IOperationDao;
import com.excilys.formation.projet.ebanking.dao.api.IOperationTypeDao;
import com.excilys.formation.projet.ebanking.dao.api.IUtilisateurDao;
import com.excilys.formation.projet.ebanking.modele.Compte;
import com.excilys.formation.projet.ebanking.modele.Operation;
import com.excilys.formation.projet.ebanking.modele.OperationType;
import com.excilys.formation.projet.ebanking.modele.Utilisateur;
import com.excilys.formation.projet.ebanking.service.api.IClientService;
import com.excilys.formation.projet.ebanking.service.api.exception.SoldeInsuffisantException;

@Service("clientService")
@Transactional(readOnly = true)
public class ClientServiceImpl implements IClientService {

	@Autowired
	IUtilisateurDao utilisateurDao;

	@Autowired
	IOperationDao operationDao;

	@Autowired
	ICompteDao compteDao;

	@Autowired
	IOperationTypeDao operationTypeDao;

	@Override
	public List<Compte> getComptesByClient(int idClient) {
		Utilisateur utilisateur = utilisateurDao.getUtilisateurFetchComptes(idClient);
		return utilisateur.getComptes();
	}

	@Override
	public List<Operation> getOperationsNonCarte(int idCompte, DateTime date, int idClient) throws AccessControlException {

		Compte compte = compteDao.getCompte(idCompte);

		Utilisateur utilisateur = utilisateurDao.getUtilisateur(idClient);

		if (compte == null || utilisateur == null) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}
		else if (!utilisateur.getComptes().contains(compte)) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}

		return operationDao.getOperationsNonCarte(idCompte, date);
	}

	@Override
	public List<Operation> getOperationsCarte(int idCompte, DateTime date, int idClient) throws AccessControlException {

		Compte compte = compteDao.getCompte(idCompte);

		Utilisateur utilisateur = utilisateurDao.getUtilisateur(idClient);

		if (compte == null || utilisateur == null) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}
		else if (!utilisateur.getComptes().contains(compte)) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}

		return operationDao.getOperationsCarte(idCompte, date);
	}

	@Override
	public Double getSommeOperationsCarte(int idCompte, DateTime date, int idClient) throws AccessControlException {

		Compte compte = compteDao.getCompte(idCompte);

		Utilisateur utilisateur = utilisateurDao.getUtilisateur(idClient);

		if (compte == null || utilisateur == null) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}
		else if (!utilisateur.getComptes().contains(compte)) {
			throw new AccessControlException("Vous ne pouvez pas accéder à cette page");
		}

		return operationDao.getSommeOperationsCarte(idCompte, date);
	}

	@Override
	@Transactional(readOnly = false)
	public void addVirementInterne(Compte compteSource, Compte compteDestination, double montant, DateTime date) throws SoldeInsuffisantException {

		Assert.notNull(compteSource, "le compte source est obligatoire");
		Assert.notNull(compteDestination, "le compte destination est obligatoire");
		Assert.state(compteDestination.getId() != compteSource.getId(), "les comptes source et destinataire doivent être différents");
		Assert.state(montant > 0, "Le montant à virer doit être positif");

		double soldePrevisionnel = compteSource.getSoldeCourant() - montant;

		if (soldePrevisionnel < 0) {
			throw new SoldeInsuffisantException("Vous êtes trop pauvre");
		}

		compteSource.setSoldeCourant(soldePrevisionnel);
		compteDao.update(compteSource);
		compteDestination.setSoldeCourant(compteDestination.getSoldeCourant() + montant);
		compteDao.update(compteDestination);

		String libelle = "Virement interne de " + compteSource.getNumero() + " a " + compteDestination.getNumero();

		OperationType operationType = operationTypeDao.getOperationTypeByCode("VIR_INT");
		Operation operationDestination = new Operation();
		operationDestination.setCompte(compteDestination);
		operationDestination.setDate(date);
		operationDestination.setLibelle(libelle);
		operationDestination.setMontant(montant);
		operationDestination.setOperationType(operationType);
		operationDao.save(operationDestination);

		Operation operationSource = new Operation();
		operationSource.setCompte(compteSource);
		operationSource.setDate(date);
		operationSource.setLibelle(libelle);
		operationSource.setMontant(-montant);
		operationSource.setOperationType(operationType);
		operationDao.save(operationSource);
	}
}
